package com.change.core.util;

import com.change.core.jackson.CustomTimeModule;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.List;
import java.util.function.Supplier;

/**
 * JSON工具类
 *
 * @author linqunxun
 * @date 2021/9/10 10:24 上午
 */
@Slf4j
public final class JSON {

    private JSON() {
    }

    private static ObjectMapper om;

    static {
        om = new ObjectMapper().findAndRegisterModules();
        om.registerModule(new CustomTimeModule());
        // 禁用时间转时间戳，在转换时间类型的字段时，结果转成字符串而非时间戳
        om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        log.debug("设置JSON工具类的ObjectMapper中，提供的Module有：{}", om.getRegisteredModuleIds());
    }

    /**
     * 对象转JSON
     * 当转换过程发生异常时，将会包装成一个运行时异常将其抛出
     *
     * @param obj 对象
     * @return JSON
     */
    public static String toJSON(Object obj) {
        return toJSON(obj, () -> new RuntimeException("数据转换失败"));
    }

    /**
     * 对象转JSON
     * 当转换过程发生异常时，将会抛出异常提供者所提供的异常
     *
     * @param obj               对象
     * @param exceptionSupplier 异常提供者
     * @return JSON
     */
    public static <X extends Throwable> String toJSON(Object obj, Supplier<? extends X> exceptionSupplier) throws X {
        try {
            return om.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.error("对象转JSON失败", e);
            throw exceptionSupplier.get();
        }
    }

    /**
     * JSON转对象
     * 当转换过程发生异常时，将会包装成一个运行时异常将其抛出
     *
     * @param json      JSON
     * @param valueType 结果类型
     * @return 指定结果类型的对象
     */
    public static <T> T fromJSON(String json, Class<T> valueType) {
        return fromJSON(json, valueType, () -> new RuntimeException("数据转换失败"));
    }

    /**
     * JSON转对象
     * 当转换过程发生异常时，将会抛出异常提供者所提供的异常
     *
     * @param json              JSON
     * @param valueType         结果类型
     * @param exceptionSupplier 异常提供者
     * @return 指定结果类型的对象
     */
    public static <T, X extends Throwable> T fromJSON(String json, Class<T> valueType, Supplier<? extends X> exceptionSupplier) throws X {
        try {
            return om.readValue(json, valueType);
        } catch (IOException e) {
            log.error("JSON转对象失败", e);
            throw exceptionSupplier.get();
        }
    }

    /**
     * 数组格式的JSON转集合
     * 当转换过程发生异常时，将会包装成一个运行时异常将其抛出
     *
     * @param jsonArray   数组格式的JSON
     * @param elementType 集合元素的类型
     * @return 泛型为指定元素类型的集合
     */
    public static <T> List<T> fromJSONArray(String jsonArray, Class<T> elementType) {
        return fromJSONArray(jsonArray, elementType, () -> new RuntimeException("数据转换失败"));
    }

    /**
     * 数组格式的JSON转集合
     * 当转换过程发生异常时，将会抛出异常提供者所提供的异常
     *
     * @param jsonArray         数组格式的JSON
     * @param elementType       集合元素的类型
     * @param exceptionSupplier 异常提供者
     * @return 泛型为指定元素类型的集合
     */
    public static <T, X extends Throwable> List<T> fromJSONArray(String jsonArray, Class<T> elementType, Supplier<? extends X> exceptionSupplier) throws X {
        JavaType valueType = om.getTypeFactory().constructParametricType(List.class, elementType);
        try {
            return om.readValue(jsonArray, valueType);
        } catch (IOException e) {
            log.error("JSON数组转集合失败", e);
            throw exceptionSupplier.get();
        }
    }

    /**
     * 将一个对象转换成指定类型的对象
     * 其过程其实是将fromValue进行序列化，然后再将其反序列成toValueType
     *
     * @param fromValue   源对象
     * @param toValueType 目标类型
     * @return 目标类型的对象
     */
    public static <T> T convert(Object fromValue, Class<T> toValueType) {
        return om.convertValue(fromValue, toValueType);
    }
}
